<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>First Phaser Game (3d version)</title>
    <link rel="stylesheet" href="/css/examples.css?ver=1.0.0" />
    <script src="/js/examples.js?ver=1.1.1"></script>
    <script src="/lib/phaser.min.js?ver=3.52.0"></script>
    <script src="/lib/enable3d/enable3d.phaserExtension.0.22.0.min.js"></script>
  </head>

  <body>
    <div id="info-text">Use A and D to move<br />and W to jump.</div>
    <script>
      const { enable3d, Scene3D, Canvas, ExtendedObject3D, THREE } = ENABLE3D

      class MainScene extends Scene3D {
        constructor() {
          super({ key: 'MainScene' })
        }

        init() {
          this.accessThirdDimension({ gravity: { x: 0, y: -20, z: 0 } })
          delete this.robot
          this.stars = []
          this.score = 0
        }

        preload() {
          this.third.load.preload('sky', '/assets/img/sky.png')
          this.load.html('star', '/assets/svg/star.svg')
        }

        async create() {
          const { lights } = await this.third.warpSpeed('-ground', '-sky', '-orbitControls')

          // adjust the camera
          this.third.camera.position.set(0, 5, 20)
          this.third.camera.lookAt(0, 0, 0)

          // enable physics debugging
          this.third.physics.debug.enable()

          // add background image
          this.third.load.texture('sky').then(sky => (this.third.scene.background = sky))

          // add score text
          this.scoreText = this.add.text(32, this.cameras.main.height - 32, 'score: 0', {
            fontSize: '32px',
            fill: '#000'
          })
          this.scoreText.setOrigin(0, 1)
          this.scoreText.depth = 1

          // add platforms
          const platformMaterial = { phong: { transparent: true, color: 0x21572f } }
          const platforms = [
            this.third.physics.add.box(
              { name: 'platform-ground', y: -2, width: 30, depth: 5, height: 2, mass: 0 },
              platformMaterial
            ),
            this.third.physics.add.box(
              { name: 'platform-right1', x: 7, y: 4, width: 15, depth: 5, mass: 0 },
              platformMaterial
            ),
            this.third.physics.add.box(
              { name: 'platform-left', x: -10, y: 7, width: 10, depth: 5, mass: 0 },
              platformMaterial
            ),
            this.third.physics.add.box(
              { name: 'platform-right2', x: 10, y: 10, width: 10, depth: 5, mass: 0 },
              platformMaterial
            )
          ]

          // add stars
          const svg = this.cache.html.get('star')
          const starShape = this.third.transform.fromSVGtoShape(svg)
          const starScale = 250
          const starPositions = [
            { x: -14, y: 8.5 },
            { x: -12, y: 8.5 },
            { x: -10, y: 8.5 },
            { x: -8, y: 8.5 },
            { x: -6, y: 8.5 },
            { x: -4, y: 0 },
            { x: -2, y: 0 },
            { x: 0, y: 5.5 },
            { x: 2, y: 5.5 },
            { x: 4, y: 5.5 },
            { x: 6, y: 11.5 },
            { x: 8, y: 11.5 },
            { x: 10, y: 11.5 },
            { x: 12, y: 11.5 },
            { x: 14, y: 11.5 }
          ]
          starPositions.forEach((pos, i) => {
            const star = this.third.add.extrude({ shape: starShape[0], depth: 120 })
            star.name = `star-${i}`
            star.scale.set(1 / starScale, 1 / -starScale, 1 / starScale)
            star.material.color.setHex(0xffd851)
            star.position.setX(pos.x)
            star.position.setY(pos.y)
            this.third.physics.add.existing(star, {
              shape: 'box',
              ignoreScale: true,
              width: 0.5,
              height: 0.5,
              depth: 0.5
            })
            star.body.setCollisionFlags(6)
            this.stars.push(star)
          })

          /**
           * Model by Tomás Laulhé (https://www.patreon.com/quaternius), modifications by Don McCurdy (https://donmccurdy.com)
           * https://threejs.org/examples/#webgl_animation_skinning_morph
           * CC-0 license
           */
          // add robot
          this.third.load.gltf('/assets/glb/robot.glb').then(gltf => {
            this.robot = new ExtendedObject3D()
            this.robot.add(gltf.scene)
            const scale = 1 / 3
            this.robot.scale.set(scale, scale, scale)

            this.robot.traverse(child => {
              if (child.isMesh) {
                child.castShadow = child.receiveShadow = true
              }
            })

            // animations
            this.third.animationMixers.add(this.robot.animation.mixer)
            gltf.animations.forEach(animation => {
              this.robot.animation.add(animation.name, animation)
            })
            this.robot.animation.play('Idle')

            this.third.add.existing(this.robot)
            this.third.physics.add.existing(this.robot, {
              shape: 'capsule',
              ignoreScale: true,
              height: 0.8,
              radius: 0.4,
              offset: { y: -0.8 }
            })
            this.robot.body.setLinearFactor(1, 1, 0)
            this.robot.body.setAngularFactor(0, 0, 0)
            this.robot.body.setFriction(0)

            this.third.camera.lookAt(this.robot.position)

            // add a sensor
            const sensor = new ExtendedObject3D()
            sensor.position.setY(-0.9)
            this.third.physics.add.existing(sensor, { mass: 1e-8, shape: 'box', width: 0.2, height: 0.2, depth: 0.2 })
            sensor.body.setCollisionFlags(4)

            // connect sensor to robot
            this.third.physics.add.constraints.lock(this.robot.body, sensor.body)

            // detect if sensor is on the ground
            sensor.body.on.collision((otherObject, event) => {
              if (/platform/.test(otherObject.name)) {
                if (event !== 'end') this.robot.userData.onGround = true
                else this.robot.userData.onGround = false
              }
            })

            // check robot overlap with star
            this.robot.body.on.collision((otherObject, event) => {
              if (/star/.test(otherObject.name)) {
                if (!otherObject.userData.dead) {
                  otherObject.userData.dead = true
                  otherObject.visible = false
                  this.score += 10
                  this.scoreText.setText(`score: ${this.score}`)
                  this.third.physics.destroy(otherObject)
                }
              }
            })
          })

          // add keys
          this.keys = {
            w: this.input.keyboard.addKey('w'),
            a: this.input.keyboard.addKey('a'),
            d: this.input.keyboard.addKey('d')
          }
        }

        walkAnimation() {
          if (this.robot.animation.current !== 'Walking') this.robot.animation.play('Walking')
        }

        idleAnimation() {
          if (this.robot.animation.current !== 'Idle') this.robot.animation.play('Idle')
        }

        update(time, delta) {
          // rotate the starts
          // (this looks strange I know, I will try to improve this in a future update)
          this.stars.forEach(star => {
            if (!star.userData.dead) {
              star.rotation.y += 0.03
              star.body.needUpdate = true
            }
          })

          if (this.robot && this.robot.body) {
            // add just the camera position
            this.third.camera.position.copy(this.robot.position).add(new THREE.Vector3(0, 5, 16))

            // get rotation of robot
            const theta = this.robot.world.theta
            this.robot.body.setAngularVelocityY(0)

            // set the speed variable
            const speed = 7

            // move left
            if (this.keys.a.isDown) {
              this.robot.body.setVelocityX(-speed)
              if (theta > -(Math.PI / 2)) this.robot.body.setAngularVelocityY(-10)
              this.walkAnimation()
            }
            // move right
            else if (this.keys.d.isDown) {
              this.robot.body.setVelocityX(speed)
              if (theta < Math.PI / 2) this.robot.body.setAngularVelocityY(10)
              this.walkAnimation()
            }
            // do not move
            else {
              this.robot.body.setVelocityX(0)
              this.idleAnimation()
            }

            // jump
            if (this.keys.w.isDown && this.robot.userData.onGround && Math.abs(this.robot.body.velocity.y) < 1e-1) {
              this.robot.animation.play('WalkJump')
              this.robot.body.applyForceY(16)
            }
          }
        }
      }

      const config = {
        type: Phaser.WEBGL,
        transparent: true,
        scale: {
          mode: Phaser.Scale.FIT,
          autoCenter: Phaser.Scale.CENTER_BOTH,
          width: window.innerWidth * Math.max(1, window.devicePixelRatio / 2),
          height: window.innerHeight * Math.max(1, window.devicePixelRatio / 2)
        },
        scene: [MainScene],
        ...Canvas()
      }

      window.addEventListener('load', () => {
        enable3d(() => new Phaser.Game(config)).withPhysics('/lib/ammo/kripken')
      })
    </script>
  </body>
</html>
